import type { AnyFn } from '@vueuse/core';
import type { ComputedRef, Ref } from 'vue';
export interface UseInfiniteFetchParams {
  pageSize: number;
  currentPage: number;
}
export type UseInfiniteFetchCallBack<T extends any[]> = (
  params: UseInfiniteFetchParams,
) => Promise<{ records: T; total: number }> | { records: T; total: number };

export interface UseInfiniteOptions<T extends any[]> {
  /**
   * 请求构造函数
   */
  fetch: UseInfiniteFetchCallBack<T>;

  /**
   * 每页数量 默认:10
   */
  initPageSize?: number;

  /**
   * 初始化当前页， 默认:1
   */
  initCurrentPage?: number;

  /**
   * records使用ref 还是 shallowRef  默认为true  即shallowRef
   */
  shallow?: boolean;

  /**
   * 初始化后是否立即请求 默认true
   */
  immediate?: boolean;

  /**
   * 刷新前清空数组 默认false
   */
  resetOnRefresh?: boolean;
}

/**
 * 分页请求返回参数
 */
export interface UseInfiniteReturn<T extends any[]> {
  /**
   * 分页数据
   */
  records: Ref<T>;

  /**
   * 当前页
   */
  currentPage: Ref<number>;

  /**
   * 每页数量
   */
  pageSize: Ref<number>;

  /**
   * 接口返回的总数量
   */
  total: Readonly<Ref<number>>;

  /**
   * 总页数
   */
  totalPage: Readonly<Ref<number>>;
  /**
   * 当前是否正在加载中
   */
  isLoading: Readonly<Ref<boolean>>;

  /**
   * 执行刷新，currentPage=1
   */
  refresh: () => Promise<void>;

  /**
   * 执行请求， currentPage自动+1
   */
  loadMore: (done?: AnyFn) => Promise<void>;

  /**
   * 当前页的数组第一项索引
   */
  startNo: ComputedRef<number>;

  /**
   * 当前为刷新中
   */
  refreshing: ComputedRef<boolean>;

  /**
   * 当前为滚屏加载中
   */
  infiniteLoading: ComputedRef<boolean>;

  /**
   * 没有下一页了（到底了）
   */
  isEnd: ComputedRef<boolean>;
}

/**
 * 滚屏分页请求
 * @param options
 * @returns
 */
export function useInfinite<T extends any[]>(options: UseInfiniteOptions<T>): UseInfiniteReturn<T> {
  const shallow = options?.shallow ?? true;
  const immediate = options?.immediate ?? true;

  const refFn = shallow ? shallowRef : ref;

  const records = refFn([] as any) as Ref<T>;
  const currentPage = ref(options.initCurrentPage ?? 1);
  const pageSize = ref(options.initPageSize ?? 10);

  const total = ref(0);
  const isLoading = ref(false);
  const totalPage = computed(() => Math.ceil(total.value / pageSize.value));
  const startNo = computed(() => (currentPage.value - 1) * pageSize.value + 1);
  const isEnd = computed(() => pageSize.value * currentPage.value >= total.value);

  const refreshing = computed(() => isLoading.value && currentPage.value === 1);
  const infiniteLoading = computed(() => isLoading.value && currentPage.value !== 1);

  const _fetch = async () => {
    isLoading.value = true;
    if (currentPage.value === 1 && options?.resetOnRefresh) {
      records.value.length = 0;
      shallow && triggerRef(records);
    }
    try {
      const res = await options.fetch({
        currentPage: currentPage.value,
        pageSize: pageSize.value,
      });
      if (currentPage.value === 1) {
        records.value.length = 0;
      }
      records.value.push(...res.records);
      total.value = Number(res.total) || 0;
      shallow && triggerRef(records);
    } finally {
      isLoading.value = false;
    }
  };

  const refresh = async (done?: AnyFn) => {
    currentPage.value = 1;
    await _fetch();
    done?.();
  };

  const loadMore = async (done?: AnyFn) => {
    if (!isEnd.value && !isLoading.value) {
      currentPage.value++;
      await _fetch();
    }
    done?.();
  };

  immediate && refresh();

  return {
    records,
    currentPage: shallowReadonly(currentPage),
    pageSize: shallowReadonly(currentPage),
    total: shallowReadonly(total),
    isLoading: shallowReadonly(isLoading),
    refresh,
    refreshing,
    infiniteLoading,
    loadMore,
    startNo,
    totalPage,
    isEnd,
  };
}
